package com.untour.im.server.service.impl;

import com.untour.im.codec.protocol.packet.request.header.RequestPacketHeader;
import com.untour.im.codec.protocol.packet.response.ResponsePacketCodec;
import com.untour.im.common.enums.ClientType;
import com.untour.im.common.enums.ParamErrorCodeEnum;
import com.untour.im.exception.BusinessException;
import com.untour.im.server.dto.UserChannelInfo;
import com.untour.im.server.service.DeviceMultiLoginService;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
public abstract class AbstractDeviceMultiLoginServiceImpl implements DeviceMultiLoginService {
    protected void updateCloudInfo(){
        //todo 同步连接信息到redis中，方便其他传递消息（需要存储当前服务的唯一识别信息）
    }

    /**
     * 获取客户端类型
     * @param header
     * @return
     */
    private ClientType getClientType(RequestPacketHeader header) {
        ClientType clientType = ClientType.getByValue(header.getClientType());
        if(null == clientType){
            throw new BusinessException(ParamErrorCodeEnum.PARAM_ERROR);
        }
        return clientType;
    }

    /**
     * 踢除channel
     * @param userChannels
     * @param clientType
     * @param channelIdUserIdMap
     */
    protected abstract void removeUserChannel(List<UserChannelInfo> userChannels,
                                              ClientType clientType,
                                              Map<String, String> channelIdUserIdMap);

    @Override
    public void saveUserChannel(ChannelHandlerContext ctx, String userId,
                                RequestPacketHeader header,
                                Map<String, List<UserChannelInfo>> userIdChannelsMap,
                                Map<String, String> channelIdUserIdMap) {
        if(this.checkNeedLoginAndKickOutUser(ctx,userId,userIdChannelsMap, channelIdUserIdMap)){
            log.warn("channel已登录:{},无需重复登录",userId);
            return;
        }
        List<UserChannelInfo> userChannels = userIdChannelsMap.get(userId);
        if (!CollectionUtils.isEmpty(userChannels)) {
            // 踢出设备
            ClientType clientType = this.getClientType(header);
            //去除其他用户
            this.removeUserChannel(userChannels,clientType, channelIdUserIdMap);
        } else {
            userChannels = new ArrayList<>();
        }
        // 需要保存客户端的信息，ip、imei、客户端类型等等
        this.setChannelAndUserCache(ctx,userId,header, userIdChannelsMap, channelIdUserIdMap,userChannels);    }

    /**
     * 检查是否已经登录
     *
     * @param ctx
     * @param userId
     * @param userIdChannelsMap
     * @param channelIdUserIdMap
     * @return
     */
    private boolean checkNeedLoginAndKickOutUser(ChannelHandlerContext ctx, String userId,
                                                 Map<String, List<UserChannelInfo>> userIdChannelsMap,
                                                 Map<String, String> channelIdUserIdMap){
        //获取连接id
        String channelId = ctx.channel().id().asLongText();
        //根据连接id获取连接的用户
        String channelIdUserId = channelIdUserIdMap.get(channelId);
        //连接已绑定用户id
        if(!StringUtils.hasText(channelIdUserId)){
            if(userId.equals(channelIdUserId)){
                //用户已使用当前channel登录，无需处理
                return true;
            }
            //踢除原userid
            channelIdUserIdMap.remove(channelId);
            //踢除原channel连接的用户
            this.removeUserIdChannel(channelId, userIdChannelsMap.get(channelIdUserId));
        }
        return false;
    }

    /**
     * 删除登录缓存
     * @param channelIdUserIdMap
     * @param uc
     */
    protected void removeChannelAndUserCache(Map<String, String> channelIdUserIdMap, UserChannelInfo uc) {
        try {
            String channelId = uc.getChannel().id().asLongText();
            channelIdUserIdMap.remove(channelId);
            // 发送下线提醒
            ResponsePacketCodec.INSTANCE.sendOfflineResponseMessage(uc.getChannel());
        } finally {
            //关闭连接通道
            uc.getChannel().close();
        }
    }

    /**
     * 设置缓存
     * @param ctx
     * @param userId
     * @param header
     * @param userIdChannelsMap
     * @param channelIdUserIdMap
     * @param userChannels
     */
    private void setChannelAndUserCache(ChannelHandlerContext ctx, String userId,
                                          RequestPacketHeader header,
                                          Map<String, List<UserChannelInfo>> userIdChannelsMap,
                                          Map<String, String> channelIdUserIdMap,
                                          List<UserChannelInfo> userChannels){
        // 需要保存客户端的信息，ip、imei、客户端类型等等
        UserChannelInfo userChannelInfo = new UserChannelInfo(header, ctx.channel());
        String channelId = userChannelInfo.getChannel().id().asLongText();
        try {
            channelIdUserIdMap.put(channelId, userId);
            userChannels.add(userChannelInfo);
            userIdChannelsMap.put(userId, userChannels);
        } catch (Exception e){
            //恢复map
            channelIdUserIdMap.remove(channelId);
            userIdChannelsMap.remove(userId);
            throw e;
        }
    }

    /**
     * 删除缓存
     * @param channelId
     * @param userChannels
     */
    private void removeUserIdChannel(String channelId,
                                                List<UserChannelInfo> userChannels){
        if(!CollectionUtils.isEmpty(userChannels)){
            List<UserChannelInfo> needRemoveList = userChannels.stream()
                    .filter(userChannelInfo -> userChannelInfo.getChannel().id().asLongText().equals(channelId))
                    .collect(Collectors.toList());
            if(!CollectionUtils.isEmpty(needRemoveList)) {
                userChannels.removeAll(needRemoveList);
            }
        }
    }

    @Override
    public void removeChannelAndUserCache(ChannelHandlerContext ctx,
                                          Map<String, List<UserChannelInfo>> userIdChannelsMap,
                                          Map<String, String> channelIdUserIdMap) {
        try {
            String channelId = ctx.channel().id().asLongText();
            String userId = channelIdUserIdMap.get(channelId);
            if(StringUtils.hasText(userId)){
                channelIdUserIdMap.remove(channelId);
                this.removeUserIdChannel(channelId, userIdChannelsMap.get(userId));
            }
        } finally {
            //关闭连接通道
            ctx.channel().close();
        }
    }
}
